//
//  Framework for a raytracer
//  File: sphere.cpp
//
//  Created for the Computer Science course "Introduction Computer Graphics"
//  taught at the University of Groningen by Tobias Isenberg.
//
//  Authors:
//    Maarten Everts
//    Jasper van de Gronde
//	  Zhe Sun
//
//  This framework is inspired by and uses code of the raytracer framework of 
//  Bert Freudenberg that can be found at
//  http://isgwww.cs.uni-magdeburg.de/graphik/lehre/cg2/projekt/rtprojekt.html 
//

#include "sphere.h"
#include <iostream>
#include <math.h>
#include "stdio.h"

#ifndef PI
#define PI 3.1415926535897932384626433832795
#endif

/************************** Sphere **********************************/

Hit Sphere::intersect(const Ray &ray)
{
    /****************************************************
    * RT1.1: INTERSECTION CALCULATION
    *
    * Given: ray, position, r
    * Sought: intersects? if true: *t
    * 
    * Insert calculation of ray/sphere intersection here. 
    *
    * You have the sphere's center (C) and radius (r) as well as
    * the ray's origin (ray.O) and direction (ray.D).
    *
    * If the ray does not intersect the sphere, return false.
    * Otherwise, return true and place the distance of the
    * intersection point from the ray origin in *t (see example).
    ****************************************************/

    // place holder for actual intersection calculation

	/*
    Vector OC = (position - ray.O).normalized();
    if (OC.dot(ray.D) < 0.999) {
        return Hit::NO_HIT();
    }
    double t = 1000;
	*/
    /****************************************************
    * RT1.2: NORMAL CALCULATION
    *
    * Given: t, C, r
    * Sought: N
    * 
    * Insert calculation of the sphere's normal at the intersection point.
    ****************************************************/

	//there are a lot of solutions on the net which basically do the same calculation. The solution used here is 
	//mainly inspired by: http://sci.tuomastonteri.fi/programming/sse/example3
	Vector N;
	Point hitpoint;
	double a, b, c, t, t1, t2, disc, discSqrt;
	
	//calculate a, b and c for calculating the disc
	a = ray.D.dot(ray.D);
	b = 2 * ray.D.dot (ray.O-position);
	c = position.dot(position) + ray.O.dot(ray.O) - 2.0 * ray.O.dot(position)- r*r;
	
	disc = b*b-4*a*c; //discriminant
	if( disc < 0 )
	{
		return Hit::NO_HIT(); //no solutions, so no hitpoints
	}

	discSqrt = sqrt(disc);
	t1 = (-0.5)*(b+discSqrt)/a;
	t2 = (-0.5)*(b-discSqrt)/a;


	if (t1 <= 0.0f && t2 <= 0.0f)
	{
		return Hit::NO_HIT();
	}

	//calculate the hitpoint with the t found previously
	t = t1 > 0 && t1 < t2 ? t1 : t2;
	hitpoint = ray.O + t*ray.D;

	//calculate the normal
	N = (hitpoint - position) / r;
	
	return Hit::Hit(t,N);	

}


// bool Sphere::isEdge(const Ray &ray)
// {
//  	Vector N;
// 	Point hitpoint;
// 	double a, b, c, t, t1, t2, disc, discSqrt;
// 	
// 	//calculate a, b and c for calculating the disc
// 	a = ray.D.dot(ray.D);
// 	b = 2 * ray.D.dot (ray.O-position);
// 	c = position.dot(position) + ray.O.dot(ray.O) - 2.0 * ray.O.dot(position)- r*r;
// 	
// 	disc = b*b-4*a*c; //discriminant
//   // one solution, on the edge
//   if (disc > 0.0 && (fabs(disc) < 250) )
//     return true;
// 
//   return false;
// }


void Sphere::mapCoord(Point p, double* u, double* v) {
	double theta, phi ;
		
	theta = acos((p.z - position.z)/r);
	phi=atan2(p.y - position.y, p.x - position.x);

	phi -= angle* (PI/180.0); 
	phi = phi < 0 ? phi + 2* PI : phi;	
	*u = phi/(2*PI);
	*v = (PI-theta)/PI;

	if(*u < 0 || *u > 1.0 || *v < 0 || *v > 1.0) {
		printf("Invallid value for u or v, u: %f, v: %f\n", *u, *v);
	}
}